/*
 * @Author: Maple
 * @Date: 2021-03-20 16:38:44
 * @LastEditors  : Shiming
 * @LastEditTime : 2022-02-15 09:41:48
 * @Description: 全局基础服务：
 * 所有实体类服务必须使用extends扩展此类
 * 几个特别的Get参数：
 * 1. _allow_anonymous=true: 匿名访问，可绕开Http拦截器验证拦截
 *    使用方法： $api_xxx = `xxx?_allow_anonymous=true`
 *
 * 2. _allow_badcode=true: 请求坏状态，可使Http拦截放还完整的HttpResponse
 *    使用方法： $api_xxx = `xxxx?_allow_badcode=true`
 *
 * 3. _custom_header="[{key: string, value: string}]": 自定义Header,
 * 一些特殊的请求，需要额外附加特别的Header时使用
 *    使用方法：
 *            const headers = [{ key: xxx, value: xxx},
 *                            { key: xxx, value: xxxx}];
 *            this.service.request(
 *                    this.encodeUrlHeader(
 *                      this.service.$api_xxx, headers))
 *
 */
import { Injectable, Injector } from '@angular/core';
import { _HttpClient } from '@delon/theme';
import { environment } from '@env/environment';
import { EnvironmentService } from '@env/environment.service';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzUploadChangeParam } from 'ng-zorro-antd/upload';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { IBase } from '../../interfaces';
import { EAFileUtil } from '../../utils';

@Injectable({
  providedIn: 'root'
})
export class BaseService implements IBase {
  // 新增实例接口地址
  public $api_add_one!: string;
  // 新增多个实例地址
  public $api_add_many!: string;
  // 修改实例接口地址
  public $api_edit_one!: string;
  // 修改多个实例地址
  public $api_edit_many!: string;
  // 删除单个实例接口地址
  public $api_del_one!: string;
  // 删除多个实例接口地址
  public $api_del_many!: string;
  // 获取多个实例接口地址
  public $api_get_many!: string;
  // 获取单个实例接口地址
  public $api_get_one!: string;
  // 获取分页数据
  public $api_get_page!: string;
  // 导出数据接口地址
  public $api_export!: string;
  // 导入数据接口地址
  public $api_import!: string;
  // 导入数据模板下载地址
  public $api_import_download_tpl!: string;
  // 获取字典选项
  public $api_get_dict!: string;
  // 上传图片
  public $api_upload_url = `/api/mdc/pbc/upload/multipartFile/file`;
  constructor(public injector: Injector) {}

  get http(): _HttpClient {
    return this.injector.get(_HttpClient);
  }

  get msgSrv(): NzMessageService {
    return this.injector.get(NzMessageService);
  }

  get fileUtil(): EAFileUtil {
    return this.injector.get(EAFileUtil);
  }

  get envSrv(): EnvironmentService {
    return this.injector.get(EnvironmentService);
  }

  /**
   * 异步请求
   * @param parameter 请求参数
   * @param url 请求路径
   * @param method 请求方法
   */
  private httpRequest(
    url: string,
    parameter: any = {},
    method: 'POST' | 'GET',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    if (paramType === 'FORM') {
      parameter = this.getFormData(parameter);
    }
    // 判断请求是否需要返回完整请求体
    const allowBadCode = this.getParams('_allow_badcode', url);
    return this.http
      .request(method, url, {
        body: paramInBody ? parameter : null,
        params: paramInBody ? null : parameter
      })
      .pipe(
        map((res: any) => {
          if (res.success === true) {
            const data = res?.data;
            if (allowBadCode) {
              return res;
            } else {
              if (data === undefined || data === null) {
                return true;
              }
              return data;
            }
          } else {
            this.msgSrv.warning(res.msg);
            return allowBadCode ? res : null;
          }
        })
      );
  }

  /**
   * 把实体对象转换为FormData
   */
  private getFormData(entity: any) {
    const formdata = new FormData();
    for (const key in entity) {
      if (Object.prototype.hasOwnProperty.call(entity, key)) {
        formdata.append(key, entity[key]);
      }
    }
    return formdata;
  }

  request(
    url: string,
    parameter: any = {},
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.httpRequest(url, parameter, method, paramInBody, paramType);
  }

  asyncRequest(
    url: string,
    parameter: any = {},
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.request(url, parameter, method, paramInBody, paramType).toPromise();
  }

  // 增
  addOne(
    parameter: any,
    url: string = this.$api_add_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.request(url, parameter, method, paramInBody, paramType);
  }

  asyncAddOne(
    parameter: any,
    url: string = this.$api_add_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.addOne(parameter, url, method, paramInBody, paramType).toPromise();
  }

  addMany(
    parameter: any[],
    url: string = this.$api_add_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.request(url, parameter, method, paramInBody, paramType);
  }

  asyncAddMany(
    parameter: any,
    url: string = this.$api_add_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.addMany(parameter, url, method, paramInBody, paramType).toPromise();
  }

  // 删
  delOne(
    parameter: any,
    url: string = this.$api_del_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.request(url, parameter, method, paramInBody, paramType);
  }

  asyncDelOne(
    parameter: any,
    url: string = this.$api_del_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.delOne(parameter, url, method, paramInBody, paramType).toPromise();
  }

  delMany(
    parameter: any[],
    url: string = this.$api_del_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.request(url, parameter, method, paramInBody, paramType);
  }

  asyncDelMany(
    parameter: any[],
    url: string = this.$api_del_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.delMany(parameter, url, method, paramInBody, paramType).toPromise();
  }

  // 改
  updateOne(
    parameter: any,
    url: string = this.$api_edit_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.request(url, parameter, method, paramInBody, paramType);
  }

  asyncUpdateOne(
    parameter: any,
    url: string = this.$api_edit_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.updateOne(parameter, url, method, paramInBody, paramType).toPromise();
  }

  updateMany(
    parameter: any[],
    url: string = this.$api_edit_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.request(url, parameter, method, paramInBody, paramType);
  }

  asyncUpdateMany(
    parameter: any[],
    url: string = this.$api_edit_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.updateMany(parameter, url, method, paramInBody, paramType).toPromise();
  }

  /**
   * 查询一个实例
   */
  getOne(
    parameter: any,
    url: string = this.$api_get_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any> {
    return this.request(url, parameter, method, paramInBody, paramType);
  }

  asyncGetOne(
    parameter: any,
    url: string = this.$api_get_one,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any> {
    return this.getOne(parameter, url, method, paramInBody, paramType).toPromise();
  }

  /**
   * 查询一个实例
   */
  getMany(
    parameter: any,
    url: string = this.$api_get_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Observable<any[]> {
    return this.httpRequest(url, parameter, method, paramInBody, paramType).pipe(
      map(res => {
        return (res as any[]) || [];
      })
    );
  }

  asyncGetMany(
    parameter: any,
    url: string = this.$api_get_many,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON'
  ): Promise<any[]> {
    return this.getMany(parameter, url, method, paramInBody, paramType).toPromise();
  }

  /**
   * @description: 异步导出文件
   * 采用this.request相同的传参方式调用
   * 支持多种传参方法及自定义头（推荐）
   * @param parameter 条件参数
   * @param url API地址
   * @param method Http方法，默认：POST
   * @param paramInBody 参数是否在body内
   * @param paramType 参数类型： JSON | FORM ， 默认：FORM
   * @param allowBadCode 允许完整响应代码
   * @param async 是否同步
   * @returns Http响应结果
   */
  exportStart(
    parameter: any,
    url: string = this.$api_export,
    method: 'POST' | 'GET' = 'POST',
    paramInBody: boolean = true,
    paramType: 'JSON' | 'FORM' = 'JSON',
    allowBadCode: boolean = true,
    async: boolean = true
  ): Observable<any> | Promise<any> {
    if (allowBadCode) {
      url += `?_allow_badcode=true`;
    }
    const response = this.httpRequest(url, parameter, method, paramInBody, paramType).pipe(
      tap(res => {
        if (res.success) {
          this.msgSrv.success(`创建下载任务成功，请前往下载任务列表下载您的文件！`);
          window.open('#/download');
        }
        return res;
      })
    );
    return async ? response.toPromise() : response;
  }

  /**
   * 导出文件
   * @param body Http body参数
   */
  exportFile(body: any = {}, url: string = this.$api_export): void {
    this.fileUtil.download(url, body, {}, 'POST');
  }

  /**
   * 异步导出文件:
   * 采用HttpClient POST方式调用
   * 仅支持POST方法，传参仅支持JSON对象（不推荐）
   * @deprecated 不建议使用，请使用exportStart方法代替
   */
  asyncExport(params: any, url: string = this.$api_export): Promise<any> {
    return this.http
      .post(url, params)
      .pipe(
        map((m: any) => {
          if (m.success) {
            this.msgSrv.success('创建导出文件任务成功，请前往下载管理中下载您的文件！');
            window.open('#/download');
            return m.data;
          } else {
            this.msgSrv.warning(m.msg);
            return false;
          }
        })
      )
      .toPromise();
  }

  openURL(url: string) {
    window.open(url);
  }

  /**
   * 直接下载文件
   */
  openDownload(url: string, body: any = {}): void {
    if (url.startsWith('/')) {
      url = url.substr(1);
    }
    let newUrl = `${environment.api}/${url}`;
    if (body && JSON.stringify(body) !== '{}') {
      newUrl = newUrl + '?';
      for (const key in body) {
        if (Object.prototype.hasOwnProperty.call(body, key)) {
          newUrl = newUrl + key + '=' + body[key] + '&';
        }
      }
    }

    if (newUrl.endsWith('&')) {
      newUrl = newUrl.substr(0, newUrl.length - 1);
    }

    window.open(newUrl);
  }

  /**
   * 下载文件
   * @param url 请求路径
   * @param body Http body参数
   * @param params Http params参数
   */
  downloadFile(url: string, body: any = {}, params: any = {}, method: 'POST' | 'GET' = 'POST'): void {
    this.fileUtil.download(url, body, params, method);
  }

  /**
   * 下载模版
   * @param body Http body参数
   * @param params Http params参数
   */
  downloadTpl(body: any = {}, params: any = {}): void {
    this.fileUtil.download(this.$api_import_download_tpl, body, params);
  }

  /**
   * 处理上传状态
   */
  handleUpload({ file, fileList }: NzUploadChangeParam): void {
    const status = file.status;
    if (status !== 'uploading') {
      // console.log(file, fileList);
    }
    if (status === 'done') {
      this.msgSrv.success(`${file.name} 上传成功.`);
    } else if (status === 'error') {
      this.msgSrv.error(`${file.name} 上传失败.`);
    }
  }

  /**
   * 获取字典
   */
  getDict(key: string): Observable<any[]> {
    return this.http.post(this.$api_get_dict, { dict: key }).pipe(
      map(res => {
        if (res.success === true) {
          return res.data.map((r: any) => {
            return {
              label: r.itemValue,
              value: r.itemKey
            };
          });
        } else {
          this.msgSrv.warning(`获取取字典【${key}】时发生错误：${res.msg || '未知错误！'}`);
          return [];
        }
      })
    );
  }

  /**
   * 编码自定义头
   * @param url 需要请求的URL
   * @param customHeader 自定义头集合
   * @returns 编码后的URL
   */
  encodeUrlHeader(url: string, customHeader: { key: string; value: string }[]): string {
    if (customHeader && customHeader.length > 0) {
      let newUrl = '';
      if (url.includes('?')) {
        newUrl = url + '&_custom_header=' + encodeURI(JSON.stringify(customHeader));
      } else {
        newUrl = url + '?_custom_header=' + encodeURI(JSON.stringify(customHeader));
      }
      return newUrl;
    }

    return url;
  }

  /**
   * 获取URL指定参数的值
   */
  private getParams(paramName: string, url: string): string {
    const paramsIndex = url.indexOf('?');
    if (paramsIndex > -1) {
      const paramsStr = url.substr(paramsIndex + 1);
      const params = paramsStr.split('&');
      const keyMap = params.find(e => e.includes(paramName));
      const value = keyMap ? keyMap.split('=')[1] : '';
      return value;
    }
    return '';
  }
}
